﻿//////////////////////////////////////////////////////////////////////////////
//
// Copyright © 1998-2024 Glodon Company Limited.
//
// Licensed under the MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the “Software”),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
// sell copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
//////////////////////////////////////////////////////////////////////////////
#include "StructureWall.h"
#include "GbmpBuiltInParameterDefinitionsNew.h"
#include "GbmpCalculatorBase.h"
#include "GcmpBuiltInParameterDefinitions.h"
#include "IBaseGraphicsElementShapeComponent.h"
#include "ICalculatorCollection.h"
#include "ICopyStrategyReporter.h"
#include "IElementParameters.h"
#include "IElementParentReporter.h"
#include "IExternalDataComponent.h"
#include "IInstance.h"
#include "IParameter.h"
#include "IParameterDefinitionLibrary.h"
#include "IRegenerator.h"
#include "IRegeneratorDataIdCreator.h"
#include "StructureBasicWall.h"
#include "StructureVariableSectionWall.h"
#include "StructureWallInput.h"

#include "IDatasetManager.h"
#include "IDataset.h"
#include "IStandardCodeSystemProxy.h"
#include "IStandardCodeItem.h"
#include "UnitUniIdentities.h"
#include "ParameterType.h"
#include "EnableCompileWarning_The_LAST_IncludeInCpp.h"




using namespace gcmp;
EXPOSE_DEBUG_MODE_EXPORT(InitializeWithoutStandardCodeParameterDefinitionCreate, GCMP_MODEL_EXPORT);
CREATE_DEBUG_MODE_EXPORT(ReportStandardCodeParameterDefiniton, L"附加BIM标准参数参数定义", gcmp::DebugModeGroup::DMGT_OTHERS, L"GDMPLab", L"2023-12-20", L"打开之后，给新创建的墙附加上BIM标准参数参数定义。");

IMPLEMENT_REGEN_FUNCTIONS(gcmp, StructureWall, Volume)
IMPLEMENT_REGEN_FUNCTIONS(gcmp, StructureWall, Length)
IMPLEMENT_REGEN_FUNCTIONS(gcmp, StructureWall, Area)
IMPLEMENT_REGEN_FUNCTIONS(gcmp, StructureWall, WallShapeChanged)

DEFINE_STRUCTURE_INSTANCE_METHODS(StructureWall);

namespace gcmp
{
    class WallShapeChangedCalculator : public GbmpCalculatorBase
    {
        DECLARE_CALCULATOR(WallShapeChangedCalculator, IInstance)
    public:
        WallShapeChangedCalculator(IDocument* pDoc, const RegenDataId& outputDataId) : GbmpCalculatorBase(pDoc, outputDataId) {}
        virtual void ReportInputDataIds(std::vector<RegenDataId> & oInputDatas) const override;
        virtual void Execute() override;
    };

    void WallShapeChangedCalculator::ReportInputDataIds(std::vector<RegenDataId> & oInputDatas) const
    {
        const IInstance* pElement = GetElement<IInstance>();
        DBG_WARN_AND_RETURN_VOID_UNLESS(pElement, L"计算器的目标对象为空",L"GDMPLab",L"2024-03-30");

        RegenDataId baseGRepRdId = pElement->GetBaseGraphicsElementShapeComponent()->GetBaseGraphicsElementShapeRdId();
        oInputDatas.push_back(baseGRepRdId);
    }

    void WallShapeChangedCalculator::Execute()
    {
        IElement* pElement = GetTarget();
        DBG_WARN_AND_RETURN_VOID_UNLESS(pElement, L"无效参数",L"GDMPLab",L"2024-03-30");

        DBG_WARN_AND_RETURN_VOID_UNLESS(pElement, L"pElement为空",L"GDMPLab",L"2024-03-30");
        IInstance* pWall = quick_cast<IInstance>(pElement);
        DBG_WARN_AND_RETURN_VOID_UNLESS(pWall, L"pInstance为空",L"GDMPLab",L"2024-03-30");

        //直墙
        if (IExternalDataComponent * pExternalDataComponent = pWall->GetExternalDataComponent())
        {
            auto pData0 = pExternalDataComponent->FindExternalData(StructureBasicWall::GetStaticClassSchema()->GetName());
            if (pData0 != nullptr)
            {
                StructureBasicWall* pWall = dynamic_cast<StructureBasicWall*>(pData0);
                pWall->GetBaseDataFW()->SetWallShapeChanged(true);
            }

            //变截面墙
            auto pData1 = pExternalDataComponent->FindExternalData(StructureVariableSectionWall::GetStaticClassSchema()->GetName());
            if (pData1 != nullptr)
            {
                StructureVariableSectionWall* pWall = dynamic_cast<StructureVariableSectionWall*>(pData1);
                pWall->GetBaseDataFW()->SetWallShapeChanged(true);
            }
        }
    }

    IMPLEMENT_CALCULATOR_CREATOR(gcmp, WallShapeChangedCalculator);
}

namespace
{
   void GetStorageTypeAndDisplayTypeByStandardCode(const std::wstring& code, ParameterStorageType& storageType, UniIdentity& paramTypeId)
   {
       IStandardCodeSystemProxy* pStandardCodeSystemProxy = IStandardCodeSystemProxy::Get();
       DBG_WARN_AND_RETURN_VOID_UNLESS(pStandardCodeSystemProxy, L"pStandardCodeSystemProxy为空",L"GDMPLab",L"2024-03-30");

       const IStandardCodeItem* pCodeItem = pStandardCodeSystemProxy->GetStandardCodeItemByCode(code);
       DBG_WARN_AND_RETURN_VOID_UNLESS(pCodeItem, L"不存在此编码的数据对象",L"GDMPLab",L"2024-03-30");
       switch (pCodeItem->GetCodeValueType())
       {
       case StandardCodeValueType::Bool:
           storageType = ParameterStorageType::Bool;
           paramTypeId = PARAMETER_TYPE(YesNo);
           break;
       case StandardCodeValueType::Double:
           storageType = ParameterStorageType::Double;
           paramTypeId = PARAMETER_TYPE(Float);
           break;
       case StandardCodeValueType::Enumeration:
           storageType = ParameterStorageType::StandardCodeReference;
           paramTypeId = PARAMETER_TYPE(ValueList);
           break;
       case StandardCodeValueType::Long:
           storageType = ParameterStorageType::Int;
           paramTypeId = PARAMETER_TYPE(Number);
           break;
       case StandardCodeValueType::ReferenceCode:
           storageType = ParameterStorageType::StandardCodeReference;
           paramTypeId = PARAMETER_TYPE(StandardCodeReference);
           break;
       case StandardCodeValueType::String:
           storageType = ParameterStorageType::String;
           paramTypeId = PARAMETER_TYPE(Text);
           break;
       case StandardCodeValueType::Value:
       case StandardCodeValueType::Invalid:
       default:
       {
           DBG_WARN(L"不支持类型",L"GDMPLab",L"2024-03-30");
           return;
       }
       }
    }
}

DATA_DEFINE(StructureWall)
{
    m_pOwnerElement = nullptr;
    SetVolume__(0);
    SetLength__(0);
    SetArea__(0);
    SetConcreteStrengthGrade__(ConcreteStrengthGradeType::CSG_C30);
    SetWallShapeChanged__(false);
}

gcmp::OwnerPtr<gcmp::StructureWall> StructureWall::Create(IInstance* pInstance, const StructureWallInput* pInput)
{
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pInstance, L"pInstance为空",L"GDMPLab",L"2024-03-30");
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pInput, L"pInput为空",L"GDMPLab",L"2024-03-30");

    OwnerPtr<StructureWall> opWall = NEW_AS_OWNER_PTR(StructureWall);
    DBG_WARN_AND_RETURN_NULLPTR_UNLESS(opWall, L"opBeam为空",L"GDMPLab",L"2024-03-30");

    opWall->SetOwnerElement(pInstance);
    opWall->SetConcreteStrengthGrade__(pInput->ConcreteType);

    if (DEBUG_MODE(ReportStandardCodeParameterDefiniton))
    {
        IDocument* pDocument = pInstance->GetDocument();
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pDocument, L"pDocument为空",L"GDMPLab",L"2024-03-30");
        IParameterDefinitionLibrary * pParameterDefinitionLibrary = IParameterDefinitionLibrary::Get(pDocument);
        DBG_WARN_AND_RETURN_NULLPTR_UNLESS(pParameterDefinitionLibrary, L"pParameterDefinitionLibrary为空",L"GDMPLab",L"2024-03-30");

        std::vector<std::wstring>  standardCodes;
        standardCodes.emplace_back(L"43111117");  //分类编码
        standardCodes.emplace_back(L"43311511");   //空间配置参数
        standardCodes.emplace_back(L"43313511");  //测试围合体ID参数
        standardCodes.emplace_back(L"43511120");  //主要材料
        standardCodes.emplace_back(L"43231717"); //厚度
        standardCodes.emplace_back(L"43511122"); //是否结构件
        for (auto & iter : standardCodes)
        {
            int paramDefId = pParameterDefinitionLibrary->GetParameterDefinitionIdByStandardCode(iter);
            if (-1 == paramDefId)
            {
                ParameterStorageType storageType = ParameterStorageType::None;
                UniIdentity paramTypeId = PARAMETER_TYPE(Invalid);
                GetStorageTypeAndDisplayTypeByStandardCode(iter, storageType, paramTypeId);
                OwnerPtr<IParameterDefinition> opParameterDefinition = IParameterDefinition::CreateStandardCodeParameterDefinition(iter, L"", storageType, UNIT(None), paramTypeId);
                pParameterDefinitionLibrary->AddParameterDefinition(TransferOwnership(opParameterDefinition));
            }
        }

        ParameterAttributes pa{ false, false, true };
        DBG_WARN_AND_RETURN_FALSE_UNLESS(pDocument, L"文档指针为空",L"GDMPLab",L"2024-03-30");
        IElementParameters * pElementParameters = pInstance->GetElementParameters();
        DBG_WARN_AND_RETURN_FALSE_UNLESS(pElementParameters, L"pElementParameters指针为空",L"GDMPLab",L"2024-03-30");
        
        //分类编码
        {
            int paramDefId = pParameterDefinitionLibrary->GetParameterDefinitionIdByStandardCode (L"43111117");
            OwnerPtr<IParameter> param = IParameter::CreateParameter(
                pDocument,
                ParameterStorageType::String,
                pa,
                pParameterDefinitionLibrary->GetParameterDefinitionUidById(paramDefId),
                ParameterProcessType::GeneralInput
            );
            param->SetValueAsString(L"31211300");// 结构墙
            pElementParameters->AddEmbeddedParameter(param.get());

            OwnerPtr<IParameter> param2 = IParameter::CreateParameter(
                pDocument,
                ParameterStorageType::Int,
                pa,
                pParameterDefinitionLibrary->GetParameterDefinitionUidById(paramDefId),
                ParameterProcessType::GeneralInput
            );
            param2->SetValueAsInt(10);// 结构墙
            pElementParameters->AddEmbeddedParameter(param2.get());
        }

        //空间配置参数
        {
            int paramDefId = pParameterDefinitionLibrary->GetParameterDefinitionIdByStandardCode(L"43311511");
            OwnerPtr<IParameter> param = IParameter::CreateParameter(
                pDocument,
                ParameterStorageType::String,
                pa,
                pParameterDefinitionLibrary->GetParameterDefinitionUidById(paramDefId),
                ParameterProcessType::GeneralInput
            );

            param->SetValueAsString(L"uuid-1589962931422-3");
            pElementParameters->AddEmbeddedParameter(param.get());
        }

        //测试围合体ID参数
        {
            int paramDefId = pParameterDefinitionLibrary->GetParameterDefinitionIdByStandardCode(L"43313511");
            OwnerPtr<IParameter> param = IParameter::CreateParameter(
                pDocument,
                ParameterStorageType::String,
                pa,
                pParameterDefinitionLibrary->GetParameterDefinitionUidById(paramDefId),
                ParameterProcessType::GeneralInput
            );

            param->SetValueAsString(L"100, 110, 120");
            pElementParameters->AddEmbeddedParameter(param.get());
        }

        //主要材料
        {
            int paramDefId = pParameterDefinitionLibrary->GetParameterDefinitionIdByStandardCode(L"43511120");
            OwnerPtr<IParameter> param = IParameter::CreateParameter(
                pDocument,
                ParameterStorageType::String,
                pa,
                pParameterDefinitionLibrary->GetParameterDefinitionUidById(paramDefId),
                ParameterProcessType::GeneralInput
            );
            param->SetValueAsString(L"43991001");
            pElementParameters->AddEmbeddedParameter(param.get());
        }
        //厚度
        {
            int paramDefId = pParameterDefinitionLibrary->GetParameterDefinitionIdByStandardCode(L"43231717");
            OwnerPtr<IParameter> param = IParameter::CreateParameter(
                pDocument,
                ParameterStorageType::Int,
                pa,
                pParameterDefinitionLibrary->GetParameterDefinitionUidById(paramDefId),
                ParameterProcessType::GeneralInput
            );
            param->SetValueAsInt(100);
            pElementParameters->AddEmbeddedParameter(param.get());
        }
        //是否结构件
        {
            int paramDefId = pParameterDefinitionLibrary->GetParameterDefinitionIdByStandardCode(L"43511122");
            OwnerPtr<IParameter> param = IParameter::CreateParameter(
                pDocument,
                ParameterStorageType::Bool,
                pa,
                pParameterDefinitionLibrary->GetParameterDefinitionUidById(paramDefId),
                ParameterProcessType::GeneralInput
            );
            param->SetValueAsBool(true);
            pElementParameters->AddEmbeddedParameter(param.get());
        }
    }

    return opWall;
}

void StructureWall::UpdateForWeakParentDeletion(const std::set<ElementId>& deletedElementIds)
{
    return;
}

void StructureWall::ReportParents(IElementParentReporter& reporter) const
{
}

DECLARE_CALCULATOR_CREATOR(InstanceVolumeCalculator)
DECLARE_CALCULATOR_CREATOR(StructureInstanceLengthCalculator)
DECLARE_CALCULATOR_CREATOR(WallAreaCalculator)

void StructureWall::GetCalculators(ICalculatorCollection * calculators) const
{
    ADD_CALCULATOR(InstanceVolumeCalculator, GetDocument(), GetVolumeRdId());
    ADD_CALCULATOR(StructureInstanceLengthCalculator, GetDocument(), GetLengthRdId());
    ADD_CALCULATOR(WallAreaCalculator, GetDocument(), GetAreaRdId());
    ADD_CALCULATOR(WallShapeChangedCalculator, GetDocument(), GetWallShapeChangedRdId());
}

void StructureWall::ReportParameterDefinitions(std::vector<int>* pParamDefIds) const
{
    DBG_WARN_AND_RETURN_VOID_UNLESS(pParamDefIds != nullptr, L"pParamDefUids为空",L"GDMPLab",L"2024-03-30");
    pParamDefIds->push_back(PARAMETER_ID(VolumeBuiltInParameter));
    pParamDefIds->push_back(PARAMETER_ID(AreaBuiltInParameter));
    pParamDefIds->push_back(PARAMETER_ID(LineFamilyLengthBuiltInParameter));
    pParamDefIds->push_back(PARAMETER_ID(ConcreteStrengthGradeBuiltInParameter));
}

OwnerPtr<IParameter> StructureWall::GetNativeParameter(int paramDefId) const
{
    OwnerPtr<IParameter> param;
    ParameterAttributes pa = {false, false, true};
    if (paramDefId == PARAMETER_ID(VolumeBuiltInParameter))
    {
        //param = NEW_AS_OWNER_PTR(Parameter, GetDocument(), ParameterStorageType::Double, false, false,
            //true, PARAMETER_UID(VolumeBuiltInParameter), ParameterProcessType::GeneralOutput);
        param = IParameter::CreateParameter(GetDocument(), ParameterStorageType::Double, pa,
            PARAMETER_UID(VolumeBuiltInParameter), ParameterProcessType::GeneralOutput);
        param->SetValueAsDouble(GetVolume__());
        return TransferOwnership(param);
    }
    else if (paramDefId == PARAMETER_ID(LineFamilyLengthBuiltInParameter))
    {
        //param = NEW_AS_OWNER_PTR(Parameter, GetDocument(), ParameterStorageType::Double, false, false,
            //true, PARAMETER_UID(LineFamilyLengthBuiltInParameter), ParameterProcessType::GeneralOutput);
        param = IParameter::CreateParameter(GetDocument(), ParameterStorageType::Double, pa,
            PARAMETER_UID(LineFamilyLengthBuiltInParameter), ParameterProcessType::GeneralOutput);
        param->SetValueAsDouble(GetLength__());
        return TransferOwnership(param);
    }
    else if (paramDefId == PARAMETER_ID(ConcreteStrengthGradeBuiltInParameter))
    {
        //param = NEW_AS_OWNER_PTR(Parameter, GetDocument(), ParameterStorageType::Int, false, false,
            //true, PARAMETER_UID(ConcreteStrengthGradeBuiltInParameter), ParameterProcessType::GeneralInput);
        param = IParameter::CreateParameter(GetDocument(), ParameterStorageType::Int, pa,
            PARAMETER_UID(ConcreteStrengthGradeBuiltInParameter), ParameterProcessType::GeneralInput);
        param->SetValueAsInt((int)GetConcreteStrengthGrade__());
        return TransferOwnership(param);
    }
    else if (paramDefId == PARAMETER_ID(AreaBuiltInParameter))
    {
        //param = NEW_AS_OWNER_PTR(Parameter, GetDocument(), ParameterStorageType::Double, false, false,
            //true, PARAMETER_UID(AreaBuiltInParameter), ParameterProcessType::GeneralOutput);
        param = IParameter::CreateParameter(GetDocument(), ParameterStorageType::Double, pa,
            PARAMETER_UID(AreaBuiltInParameter), ParameterProcessType::GeneralOutput);
        param->SetValueAsDouble(GetArea__());
        return TransferOwnership(param);
    }

    return nullptr;
}

bool StructureWall::SetNativeParameter(const IParameter *param, std::wstring* errorMsg/* = nullptr*/)
{
    DBG_WARN_AND_RETURN_FALSE_UNLESS(param, L"param is null.",L"GDMPLab",L"2024-03-30");

    if (param->GetParameterDefinitionId() == PARAMETER_ID(VolumeBuiltInParameter))
    {
        SetVolume(param->GetValueAsDouble());
        return true;
    }
    
    else if (param->GetParameterDefinitionId() == PARAMETER_ID(LineFamilyLengthBuiltInParameter))
    {
        SetLength(param->GetValueAsDouble());
        return true;
    }
    else if (param->GetParameterDefinitionId() == PARAMETER_ID(ConcreteStrengthGradeBuiltInParameter))
    {
        SetConcreteStrengthGrade((ConcreteStrengthGradeType)param->GetValueAsInt());
        return true;
    }
    else if (param->GetParameterDefinitionId() == PARAMETER_ID(AreaBuiltInParameter))
    {
        SetArea(param->GetValueAsDouble());
        return true;
    }
    return false;
}

bool StructureWall::IsParameterModifiable(int paramDefId) const
{
    return true;
}

void StructureWall::Report(const ICopyContext & copyContext, ICopyStrategyReporter& reporter) const
{
    reporter.ReportToCopy(GetOwnerElementId());
}

IDocument* StructureWall::GetDocument() const
{
    return m_pOwnerElement ? m_pOwnerElement->GetDocument() : nullptr;
}

ElementId StructureWall::GetOwnerElementId() const
{
    return m_pOwnerElement ? m_pOwnerElement->GetElementId() : ElementId::InvalidID;
}

NdbObject* StructureWall::GetTopOwnerObject() const
{
    if (!GetDocument())
        return nullptr;

    return quick_cast<NdbObject>(GetDocument()->GetElement(GetOwnerElementId()));
}